// 将 Token 转换成 AST
export function parser(tokens) {
  let token = null;
  const rawTokens = [];

  const next = (mode) => {
    token = tokens.next(mode);

    if (!token) {
      throw new TypeError("next token is undefined");
    }
    rawTokens.push(token);
    if (
      token.type === "CommentToken" ||
      token.type === "Whitespace" ||
      token.type === "Newline"
    ) {
      return next(mode);
    }
    // console.log("parser: ", token && token.type);
  };

  const panic = (message) => {
    throw new SyntaxError(
      `${message} at
        ${token.loc.file}:${token.loc.start.line}:${token.loc.start.column}`
    );
  };

  const ValueLiteral = () => {
    if (
      token.type === "Id" ||
      token.type === "NumbericLiteral" ||
      token.type === "String" ||
      token.type === "RegExpToken"
    ) {
      const _token = token;
      next();
      return _token;
    }

    return null;
  };

  const ValueLiteralMust = () => {
    const next = ValueLiteral();

    if (!next) {
      // throw new SyntaxError(
      //   `Expected token type "NumbericLiteral" or "RegExpLiteral" got "${token.type}" at
      //     ${token.loc.file}:${token.loc.start.line}:${token.loc.start.column}`
      // );
      panic(
        `Expected token type "NumbericLiteral" or "RegExpLiteral" got "${token.type}"`
      );
    }

    return next;
  };

  //#region *
  const MulToken = () => {
    if (token.type === "MulToken") {
      const _token = token;
      next("expression");
      return _token;
    }

    return null;
  };

  const MulExpression = (left) => {
    const op = MulToken() || DivToken();
    if (!op) return left;
    const right = ValueLiteralMust();
    const node = {
      type: "BinaryExpression",
      left,
      operatorToken: op,
      right,
      // TODO: loc
      loc: {
        file: op.loc.file,
        start: left.loc.start,
        end: right.loc.end,
      },
    };

    return MulExpression(node);
  };
  //#endregion

  //#region /
  const DivToken = () => {
    if (token.type === "DivToken") {
      const _token = token;
      next("expression");
      return _token;
    }

    return null;
  };
  //#endregion

  const take = (type, mode) => {
    if (token.type === type) {
      const _token = token;
      next(mode);
      return _token;
    }

    // throw new SyntaxError(
    //   `Expected token type "${type}" got "${token.type}" at
    //     ${token.loc.file}:${token.loc.start.line}:${token.loc.start.column}`
    // );
    panic(`Expected token type "${type}" got "${token.type}"`);
  };
  const maybeTake = (type, mode) => {
    if (token.type === type) {
      const _token = token;
      next(mode);
      return _token;
    }

    return null;
  };

  //#region +
  const PlusToken = () => {
    if (token.type === "PlusToken") {
      const _token = token;
      next("expression");
      return _token;
    }

    return null;
  };

  const PlusExpression = (left) => {
    const op = PlusToken();
    if (!op) return left;
    const next = ValueLiteralMust();

    const right = MulExpression(next);
    const node = {
      type: "BinaryExpression",
      left,
      operatorToken: op,
      right,
      // TODO: loc
      loc: {
        file: op.loc.file,
        start: left.loc.start,
        end: right.loc.end,
      },
    };

    return PlusExpression(node);
  };
  //#endregion

  const BinaryExpression = () => {
    const head = ValueLiteral();
    if (!head) return null;

    return PlusExpression(MulExpression(head)); // 这里可以处理 括号啥的的传递处理
  };

  const Expression = () => {
    return BinaryExpression();
  };

  // 处理函数体
  const Block = () => {
    const open = maybeTake("OpenCurly", "expression"); // 左大括号
    if (!open) return null;
    const body = Statements(); // 函数体
    const close = take("CloseCurly", "expression"); // 右大括号

    return {
      type: "Block",
      body,
      loc: {
        file: open.loc.file,
        start: open.loc.start,
        end: close.loc.start,
      },
    };
  };

  // 处理 if else
  const IfStatement = () => {
    const kw = maybeTake("If");
    if (!kw) return null;
    take("OpenParent", "expression");
    const condition = Expression();
    if (!condition) {
      panic("Expected an Expression for condition");
    }
    take("CloseParent", "expression");
    const then = Block() || Statement();
    if (!then) {
      panic("Expected an Expression for then");
    }
    let els = null;
    const elseKw = maybeTake("Else", "expression");
    if (elseKw) {
      els = Block() || Statement();
      if (!els) {
        panic("Expected an Expression for else");
      }
    }

    const end = els ? els.loc.end : then.loc.end;

    return {
      type: "If",
      condition,
      then,
      else: els,
      loc: {
        file: kw.loc.file,
        start: kw.loc.start,
        end,
      },
    };
  };
  // 处理 function 参数，及逗号
  const ArgumentList = () => {
    let args = [];
    take("OpenParent"); // 左小括号

    const id = maybeTake("Id");
    if (id) {
      args.push(id);
    }

    for (; ;) {
      const colon = maybeTake("Colon"); // 逗号
      if (!colon) break;
      const id = take("Id");
      args.push(id);
    }

    take("CloseParent"); // 右小括号

    return args;
  };

  // 处理 function
  const FunctionStatement = () => {
    const kw = maybeTake("Function");
    if (!kw) return null;

    const name = take("Id");
    const args = ArgumentList();

    const body = Block();
    if (!body) {
      panic("Expected a Block for the fucntion");
    };

    return {
      type: "FunctionStatement",
      name,
      args,
      body,
      loc: {
        file: kw.loc.file,
        start: kw.loc.start,
        end: body.loc.end,
      },
    };
  };

  const Statement = () => {
    const expression = Expression();
    if (expression) {
      const sc = take("Semicolon", "expression");
      // return expression;
      return {
        type: "Statement",
        expression,
        loc: {
          file: expression.loc.file,
          start: expression.loc.start,
          end: sc.loc.end,
        },
      };
    }

    // if...else
    const ifstmt = IfStatement();
    if (ifstmt) {
      maybeTake("Semicolon", "expression");
      return ifstmt;
    }
    // function
    const fnstmt = FunctionStatement();
    if (fnstmt) {
      return fnstmt;
    }

    return null;
  };

  const Statements = () => {
    const stmts = [];
    for (; ;) {
      const stmt = Statement();
      if (!stmt) break;
      stmts.push(stmt);
    }

    return stmts;
  };

  next("expression");
  const ast = Statements();

  if (token.type !== "EndOfFileToken") {
    // throw new SyntaxError(
    //   `Expected token type "EndOfFileToken" got "${token.type}" at
    //     ${token.loc.file}:${token.loc.start.line}:${token.loc.start.column}`
    // );
    panic(`Expected token type "EndOfFileToken" got "${token.type}"`);
  }

  return {
    ast,
    tokens: rawTokens,
  };
}
